'use strict'
var e,
	t = (e = require('crypto')) && 'object' == typeof e && 'default' in e ? e.default : e
const n = {
	TOKEN_EXPIRED: 'uni-id-token-expired',
	CHECK_TOKEN_FAILED: 'uni-id-check-token-failed',
	PARAM_REQUIRED: 'uni-id-param-required',
	ACCOUNT_EXISTS: 'uni-id-account-exists',
	ACCOUNT_NOT_EXISTS: 'uni-id-account-not-exists',
	ACCOUNT_CONFLICT: 'uni-id-account-conflict',
	ACCOUNT_BANNED: 'uni-id-account-banned',
	ACCOUNT_AUDITING: 'uni-id-account-auditing',
	ACCOUNT_AUDIT_FAILED: 'uni-id-account-audit-failed',
	ACCOUNT_CLOSED: 'uni-id-account-closed'
}
function i(e) {
	return !!e && ('object' == typeof e || 'function' == typeof e) && 'function' == typeof e.then
}
function r(e) {
	if (!e) return
	const t = e.match(/^(\d+).(\d+).(\d+)/)
	return t ? t.slice(1, 4).map(e => parseInt(e)) : void 0
}
function o(e, t) {
	const n = r(e),
		i = r(t)
	return n
		? i
			? (function (e, t) {
					const n = Math.max(e.length, t.length)
					for (let i = 0; i < n; i++) {
						const n = e[i],
							r = t[i]
						if (n > r) return 1
						if (n < r) return -1
					}
					return 0
				})(n, i)
			: 1
		: i
			? -1
			: 0
}
const s = { 'uni-id-token-expired': 30203, 'uni-id-check-token-failed': 30202 }
function c(e) {
	const { errCode: t, errMsgValue: n } = e
	;(e.errMsg = this._t(t, n)), t in s && (e.code = s[t]), delete e.errMsgValue
}
function a(e) {
	return 'object' === ((i = e), Object.prototype.toString.call(i).slice(8, -1).toLowerCase()) && e.errCode && ((t = e.errCode), Object.values(n).includes(t)) && !!e.errCode
	var t, i
}
let u = {
	'zh-Hans': {
		'uni-id-token-expired': '登录状态失效，token已过期',
		'uni-id-check-token-failed': 'token校验未通过',
		'uni-id-param-required': '缺少参数: {param}',
		'uni-id-account-exists': '此账号已注册',
		'uni-id-account-not-exists': '此账号未注册',
		'uni-id-account-conflict': '用户账号冲突',
		'uni-id-account-banned': '从账号已封禁',
		'uni-id-account-auditing': '此账号正在审核中',
		'uni-id-account-audit-failed': '此账号审核失败',
		'uni-id-account-closed': '此账号已注销'
	},
	en: {
		'uni-id-token-expired': 'The login status is invalid, token has expired',
		'uni-id-check-token-failed': 'Check token failed',
		'uni-id-param-required': 'Parameter required: {param}',
		'uni-id-account-exists': 'Account exists',
		'uni-id-account-not-exists': 'Account does not exists',
		'uni-id-account-conflict': 'User account conflict',
		'uni-id-account-banned': 'Account has been banned',
		'uni-id-account-auditing': 'Account audit in progress',
		'uni-id-account-audit-failed': 'Account audit failed',
		'uni-id-account-closed': 'Account has been closed'
	}
}
try {
	const e = require.resolve('uni-config-center/uni-id/lang/index.js')
	u = (function (e, t) {
		const n = Object.keys(e)
		n.push(...Object.keys(t))
		const i = {}
		for (let r = 0; r < n.length; r++) {
			const o = n[r]
			i[o] = Object.assign({}, e[o], t[o])
		}
		return i
	})(u, require(e))
} catch (e) {}
var d = u
function l(e) {
	return e.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
}
function h(e) {
	return JSON.parse(
		((t = (function (e) {
			var t = 4 - ((e = e.toString()).length % 4)
			if (4 !== t) for (var n = 0; n < t; ++n) e += '='
			return e.replace(/-/g, '+').replace(/_/g, '/')
		})(e)),
		Buffer.from(t, 'base64').toString('utf-8'))
	)
	var t
}
function f(e) {
	return l(((t = JSON.stringify(e)), Buffer.from(t, 'utf-8').toString('base64')))
	var t
}
function p(e, n) {
	return l(t.createHmac('sha256', n).update(e).digest('base64'))
}
const k = function (e, t) {
		if ('string' != typeof e) throw new Error('Invalid token')
		const n = e.split('.')
		if (3 !== n.length) throw new Error('Invalid token')
		const [i, r, o] = n
		if (p(i + '.' + r, t) !== o) throw new Error('Invalid token')
		const s = h(i)
		if ('HS256' !== s.alg || 'JWT' !== s.typ) throw new Error('Invalid token')
		const c = h(r)
		if (1e3 * c.exp < Date.now()) {
			const e = new Error('Token expired')
			throw ((e.name = 'TokenExpiredError'), e)
		}
		return c
	},
	g = function (e, t, n = {}) {
		const { expiresIn: i } = n
		if (!i) throw new Error('expiresIn is required')
		const r = parseInt(Date.now() / 1e3),
			o = { ...e, iat: r, exp: r + n.expiresIn },
			s = f({ alg: 'HS256', typ: 'JWT' }) + '.' + f(o)
		return s + '.' + p(s, t)
	},
	_ = uniCloud.database(),
	I = _.command,
	C = _.collection('uni-id-users'),
	T = _.collection('uni-id-roles')
class E {
	constructor({ uniId: e } = {}) {
		;(this.uid = null),
			(this.userRecord = null),
			(this.userPermission = null),
			(this.oldToken = null),
			(this.oldTokenPayload = null),
			(this.uniId = e),
			(this.config = this.uniId._getConfig()),
			(this.clientInfo = this.uniId._clientInfo),
			this.checkConfig()
	}
	checkConfig() {
		const { tokenExpiresIn: e, tokenExpiresThreshold: t } = this.config
		if (t >= e) throw new Error('Config error, tokenExpiresThreshold should be less than tokenExpiresIn')
		t > e / 2 && console.warn(`Please check whether the tokenExpiresThreshold configuration is set too large, tokenExpiresThreshold: ${t}, tokenExpiresIn: ${e}`)
	}
	get customToken() {
		return this.uniId.interceptorMap.get('customToken')
	}
	isTokenInDb(e) {
		return o(e, '1.0.10') >= 0
	}
	async getUserRecord() {
		if (this.userRecord) return this.userRecord
		const e = await C.doc(this.uid).get()
		if (((this.userRecord = e.data[0]), !this.userRecord)) throw { errCode: n.ACCOUNT_NOT_EXISTS }
		switch (this.userRecord.status) {
			case void 0:
			case 0:
				break
			case 1:
				throw { errCode: n.ACCOUNT_BANNED }
			case 2:
				throw { errCode: n.ACCOUNT_AUDITING }
			case 3:
				throw { errCode: n.ACCOUNT_AUDIT_FAILED }
			case 4:
				throw { errCode: n.ACCOUNT_CLOSED }
		}
		if (this.oldTokenPayload) {
			if (this.isTokenInDb(this.oldTokenPayload.uniIdVersion)) {
				if (-1 === (this.userRecord.token || []).indexOf(this.oldToken)) throw { errCode: n.CHECK_TOKEN_FAILED }
			}
			if (this.userRecord.valid_token_date && this.userRecord.valid_token_date > 1e3 * this.oldTokenPayload.iat) throw { errCode: n.TOKEN_EXPIRED }
		}
		return this.userRecord
	}
	async updateUserRecord(e) {
		await C.doc(this.uid).update(e)
	}
	async getUserPermission() {
		if (this.userPermission) return this.userPermission
		const e = (await this.getUserRecord()).role || []
		if (0 === e.length) return (this.userPermission = { role: [], permission: [] }), this.userPermission
		if (e.includes('admin')) return (this.userPermission = { role: e, permission: [] }), this.userPermission
		const t = await T.where({ role_id: I.in(e) }).get(),
			n = ((i = t.data.reduce((e, t) => (t.permission && e.push(...t.permission), e), [])), Array.from(new Set(i)))
		var i
		return (this.userPermission = { role: e, permission: n }), this.userPermission
	}
	async _createToken({ uid: e, role: t, permission: i } = {}) {
		if (!t || !i) {
			const e = await this.getUserPermission()
			;(t = e.role), (i = e.permission)
		}
		let r = { uid: e, role: t, permission: i }
		if (this.uniId.interceptorMap.has('customToken')) {
			const n = this.uniId.interceptorMap.get('customToken')
			if ('function' != typeof n) throw new Error('Invalid custom token file')
			r = await n({ uid: e, role: t, permission: i })
		}
		const o = Date.now(),
			{ tokenSecret: s, tokenExpiresIn: c, maxTokenLength: a = 10 } = this.config,
			u = g({ ...r, uniIdVersion: '1.0.18' }, s, { expiresIn: c }),
			d = await this.getUserRecord(),
			l = (d.token || []).filter(e => {
				try {
					const t = this._checkToken(e)
					if (d.valid_token_date && d.valid_token_date > 1e3 * t.iat) return !1
				} catch (e) {
					if (e.errCode === n.TOKEN_EXPIRED) return !1
				}
				return !0
			})
		return (
			l.push(u),
			l.length > a && l.splice(0, l.length - a),
			await this.updateUserRecord({ last_login_ip: this.clientInfo.clientIP, last_login_date: o, token: l }),
			{ token: u, tokenExpired: o + 1e3 * c }
		)
	}
	async createToken({ uid: e, role: t, permission: i } = {}) {
		if (!e) throw { errCode: n.PARAM_REQUIRED, errMsgValue: { param: 'uid' } }
		this.uid = e
		const { token: r, tokenExpired: o } = await this._createToken({ uid: e, role: t, permission: i })
		return { errCode: 0, token: r, tokenExpired: o }
	}
	async refreshToken({ token: e } = {}) {
		if (!e) throw { errCode: n.PARAM_REQUIRED, errMsgValue: { param: 'token' } }
		this.oldToken = e
		const t = this._checkToken(e)
		;(this.uid = t.uid), (this.oldTokenPayload = t)
		const { uid: i } = t,
			{ role: r, permission: o } = await this.getUserPermission(),
			{ token: s, tokenExpired: c } = await this._createToken({ uid: i, role: r, permission: o })
		return { errCode: 0, token: s, tokenExpired: c }
	}
	_checkToken(e) {
		const { tokenSecret: t } = this.config
		let i
		try {
			i = k(e, t)
		} catch (e) {
			if ('TokenExpiredError' === e.name) throw { errCode: n.TOKEN_EXPIRED }
			throw { errCode: n.CHECK_TOKEN_FAILED }
		}
		return i
	}
	async checkToken(e, { autoRefresh: t = !0 } = {}) {
		if (!e) throw { errCode: n.CHECK_TOKEN_FAILED }
		this.oldToken = e
		const i = this._checkToken(e)
		;(this.uid = i.uid), (this.oldTokenPayload = i)
		const { tokenExpiresThreshold: r } = this.config,
			{ uid: o, role: s, permission: c } = i,
			a = { role: s, permission: c }
		if (!s && !c) {
			const { role: e, permission: t } = await this.getUserPermission()
			;(a.role = e), (a.permission = t)
		}
		if (!r || !t) {
			const e = { code: 0, errCode: 0, ...i, ...a }
			return delete e.uniIdVersion, e
		}
		const u = Date.now()
		let d = {}
		1e3 * i.exp - u < 1e3 * r && (d = await this._createToken({ uid: o }))
		const l = { code: 0, errCode: 0, ...i, ...a, ...d }
		return delete l.uniIdVersion, l
	}
}
var m = Object.freeze({
	__proto__: null,
	checkToken: async function (e, { autoRefresh: t = !0 } = {}) {
		return new E({ uniId: this }).checkToken(e, { autoRefresh: t })
	},
	createToken: async function ({ uid: e, role: t, permission: n } = {}) {
		return new E({ uniId: this }).createToken({ uid: e, role: t, permission: n })
	},
	refreshToken: async function ({ token: e } = {}) {
		return new E({ uniId: this }).refreshToken({ token: e })
	}
})
const w = require('uni-config-center')({ pluginId: 'uni-id' })
class x {
	constructor({ context: e, clientInfo: t, config: n } = {}) {
		;(this._clientInfo = e
			? (function (e) {
					return { appId: e.APPID, platform: e.PLATFORM, locale: e.LOCALE, clientIP: e.CLIENTIP, deviceId: e.DEVICEID }
				})(e)
			: t),
			(this._config = n),
			(this.config = this._getOriginConfig()),
			(this.interceptorMap = new Map()),
			w.hasFile('custom-token.js') && this.setInterceptor('customToken', require(w.resolve('custom-token.js')))
		;(this._i18n = uniCloud.initI18n({ locale: this._clientInfo.locale, fallbackLocale: 'zh-Hans', messages: JSON.parse(JSON.stringify(d)) })),
			d[this._i18n.locale] || this._i18n.setLocale('zh-Hans')
	}
	setInterceptor(e, t) {
		this.interceptorMap.set(e, t)
	}
	_t(...e) {
		return this._i18n.t(...e)
	}
	_parseOriginConfig(e) {
		return Array.isArray(e) ? e : e[0] ? Object.values(e) : e
	}
	_getOriginConfig() {
		if (this._config) return this._config
		if (w.hasFile('config.json')) {
			let e
			try {
				e = w.config()
			} catch (e) {
				throw new Error('Invalid uni-id config file\n' + e.message)
			}
			return this._parseOriginConfig(e)
		}
		try {
			return this._parseOriginConfig(require('uni-id/config.json'))
		} catch (e) {
			throw new Error('Invalid uni-id config file')
		}
	}
	_getAppConfig() {
		const e = this._getOriginConfig()
		return Array.isArray(e) ? e.find(e => e.dcloudAppid === this._clientInfo.appId) || e.find(e => e.isDefaultConfig) : e
	}
	_getPlatformConfig() {
		const e = this._getAppConfig()
		if (!e) throw new Error(`Config for current app (${this._clientInfo.appId}) was not found, please check your config file or client appId`)
		let t
		switch (
			(['app-plus', 'app-android', 'app-ios'].indexOf(this._clientInfo.platform) > -1 && (this._clientInfo.platform = 'app'),
			'h5' === this._clientInfo.platform && (this._clientInfo.platform = 'web'),
			this._clientInfo.platform)
		) {
			case 'web':
				t = 'h5'
				break
			case 'app':
				t = 'app-plus'
		}
		const n = [{ tokenExpiresIn: 7200, tokenExpiresThreshold: 1200, passwordErrorLimit: 6, passwordErrorRetryTime: 3600 }, e]
		t && e[t] && n.push(e[t]), n.push(e[this._clientInfo.platform])
		const i = Object.assign(...n)
		return (
			['tokenSecret', 'tokenExpiresIn'].forEach(e => {
				if (!i || !i[e]) throw new Error(`Config parameter missing, ${e} is required`)
			}),
			i
		)
	}
	_getConfig() {
		return this._getPlatformConfig()
	}
}
for (const e in m) x.prototype[e] = m[e]
function y(e) {
	const t = new x(e)
	return new Proxy(t, {
		get(e, t) {
			if (t in e && 0 !== t.indexOf('_')) {
				if ('function' == typeof e[t])
					return ((n = e[t]),
					function () {
						let e
						try {
							e = n.apply(this, arguments)
						} catch (e) {
							if (a(e)) return c.call(this, e), e
							throw e
						}
						return i(e)
							? e.then(
									e => (a(e) && c.call(this, e), e),
									e => {
										if (a(e)) return c.call(this, e), e
										throw e
									}
								)
							: (a(e) && c.call(this, e), e)
					}).bind(e)
				if ('context' !== t && 'config' !== t) return e[t]
			}
			var n
		}
	})
}
x.prototype.createInstance = y
const O = { createInstance: y }
module.exports = O
